﻿using AspectCore.Configuration;
using AspectCore.DynamicProxy;
using AspectCore.Extensions.Autofac;
using AspectCore.Injector;
using Autofac;
using Consul;
using Exceptionless;
using Finbook.ConsoleApp.Data;
using Finbook.ConsoleApp.Interceptors;
using Finbook.ConsoleApp.Jobs;
using Finbook.ConsoleApp.Models;
using Finbook.ConsoleApp.Services;
using Finbook.Entity;
using Finbook.Entity.Cmds;
using Infrastructure;
using JWT;
using JWT.Algorithms;
using JWT.Builder;
using JWT.Serializers;
using MassTransit;
using MongoDB.Bson;
using MongoDB.Driver;
using Polly;
using Quartz;
using Quartz.Impl;
using Quartz.Spi;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;

namespace Finbook.ConsoleApp
{
    class Program
    {
        private static IContainer _container;
        private static string RedisConnectionString = "140.143.88.100:6379,password=p@ssw0rd,connectTimeout=1000";
        //private static string RedisConnectionString = "116.196.97.224:6379";
        private static string ProjectName = "PPM.VerticalSite";
        private static string DomainName = "WaresOnlineDomain";
        private static string KeyPrefix = $"{ProjectName}:{DomainName}";
        static Program()
        {
            var container = new ServiceContainer();
            container.AddType<ITestService, TestService>();
            var builder = new ContainerBuilder();
            //builder.Populate(container);
            builder.RegisterType<ITestService>();
            builder.RegisterType<IJob>();
            builder.RegisterDynamicProxy(container.Configuration, cfg =>
             {
                 cfg.Interceptors.AddTyped<HystrixCommandInterceptorAttribute>(Predicates.ForService("*Service"));
             });
            _container = builder.Build();
        }
        static void Main(string[] args)
        {
            try
            {
                //Test();
                //PortTest();
                //PollyTest();
                //PollyTest2();
                //AspectTest();
                //JwtTest();
                //HystrixCommandInterceptorAttrTest();
                //MongoDBTestAsync().Wait();
                //RedisTestAsync().Wait();
                //RedisClickCountAsync().Wait();
                //RedisListTestAsync().Wait();
                //RedisListQueueTestAsync().Wait();
                //RedisSetTestAsync().Wait();
                //RedisSortedSetTestAsync().Wait();
                //RedisHashTestAsync().Wait();
                //RedisBatchTestAsync().Wait();
                //RedisDistributedLockTestAsync().Wait();
                //RedisGeoTestAsync().Wait();
                //RedisGrabRedEnvelopeTestAsync().Wait();
                //ImageProcessorTestAsync();
                //QuartzTestAsync().Wait();
                //ExceptionlessTest();
                MasstransitCmdTest();
                RupengSmsTestAsync().Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"//******异常信息:{ex.Message}堆栈信息:{ex.StackTrace}");
            }
            Console.WriteLine("Hello World!");
            Console.Read();
        }

        private static async Task RupengSmsTestAsync()
        {
            var userName = Uri.EscapeDataString("rupengtest1");
            var appKey = Uri.EscapeDataString("fdsafasdf@adfasdfa");
            var templateId = "182";
            var code = Uri.EscapeDataString("251146");
            var phoneNum = "18801385563";
            var url = $"http://sms.rupeng.cn/SendSms.ashx?userName={userName}&appKey={appKey}&templateId={templateId}&code={code}&phoneNum={phoneNum}";
            var rt = await new HttpClient().GetAsync(url);
            var str = await rt.Content.ReadAsStringAsync();
        }

        private static void MasstransitCmdTest()
        {
            var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                var host = cfg.Host(new Uri(ConfigSettings.ServerUrl), hostCfg =>
                  {
                      hostCfg.Username(ConfigSettings.UserName);
                      hostCfg.Password(ConfigSettings.Password);
                  });
                cfg.ReceiveEndpoint(host, "eventQueue", endPointCfg =>
                  {
                      endPointCfg.Consumer<TestPublishedEventHandler>();
                      endPointCfg.Consumer<TestPublishedEventAnotherHandler>();
                  });
                cfg.ReceiveEndpoint(host, "cmdQueue", endPointCfg =>
                {
                    endPointCfg.Consumer<TestPublishedEventHandler>();
                    endPointCfg.Consumer<TestPublishedEventAnotherHandler>();
                });
                cfg.ReceiveEndpoint(host, "requestQueue", endPointCfg =>
                {
                    endPointCfg.Consumer<TestRequestHandler>();
                    endPointCfg.Observer<TestRequest>(new TestRequestObserver());
                });
            });
            bus.Start();
        }


        private static void ExceptionlessTest()
        {
            ExceptionlessClient.Default.SubmitLog("This is my first exceptionless log.");
        }

        private static async Task QuartzTestAsync()
        {
            IScheduler scheduler = await StdSchedulerFactory.GetDefaultScheduler();
            IJobDetail jobDetailImpl = new JobDetailImpl("testjob", typeof(TestJob));
            IMutableTrigger cronTrigger = CronScheduleBuilder.DailyAtHourAndMinute(10, 35)
                .Build();
            IMutableTrigger intervalTrigger = CalendarIntervalScheduleBuilder.Create()
                .WithInterval(3, IntervalUnit.Second)
                .Build();
            cronTrigger.Key = new TriggerKey("testTrigger", "1");
            intervalTrigger.Key = new TriggerKey("testTrigger", "1");
            await scheduler.ScheduleJob(jobDetailImpl, intervalTrigger);
            await scheduler.Start();
        }

        private static void ImageProcessorTestAsync()
        {
            ImageProcessor.GenerateVerifyCode("AB123", "d://verify.jpg");
        }

        private static async Task RedisGrabRedEnvelopeTestAsync()
        {
            using (var myConn = await ConnectionMultiplexer.ConnectAsync(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                var total = "100.00";
                var n = 1000;
                var distributions = GetDistribution(total, n);
                distributions.ToList().ForEach(d => Console.WriteLine($"{d}\t\n\t"));
            }
        }

        private static int[] GetDistribution(string total, int n)
        {
            double.TryParse(total, out double m);
            int amount = (int)(m * 1000);
            var avg = amount / n;
            var reminder = amount - avg * n;
            var random = new Random();
            var distributions = new int[n];
            for (int i = 0; i < n; i++)
            {
                distributions[i] = avg;
            }
            distributions[random.Next(0, n)] += reminder;
            for (int i = 0; i < n; i++)
            {
                var i1 = random.Next(0, n);
                var i2 = random.Next(0, n);
                var randomAmount = random.Next(0, avg / 2);
                var last = distributions[i1];
                distributions[i1] -= randomAmount;
                if (distributions[i1] < 0)
                {
                    distributions[i1] = last;
                    continue;
                }
                distributions[i2] += randomAmount;
            }
            var sum = distributions.Sum();
            Console.WriteLine(sum);
            return distributions;
        }

        private static async Task RedisGeoTestAsync()
        {
            using (var myConn = await ConnectionMultiplexer.ConnectAsync(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                var batch = db.CreateBatch();
                var key = $"{KeyPrefix}:geo";
                await db.GeoAddAsync(key, 118.631744, 32.155850, 1);
                await db.GeoAddAsync(key, 118.722382, 32.179100, 2);
                await db.GeoAddAsync(key, 118.696289, 31.985948, 3);
                await db.GeoAddAsync(key, 118.700409, 32.044169, 4);
                await db.GeoAddAsync(key, 118.810272, 31.902044, 5);
                await db.GeoAddAsync(key, 118.484802, 31.945171, 6);
                await db.GeoAddAsync(key, 119.013519, 32.024378, 7);
                await db.GeoAddAsync(key, 119.142609, 32.043005, 8);
                batch.Execute();
                var distance = await db.GeoDistanceAsync(key, 1, 2, GeoUnit.Kilometers);
                Console.WriteLine(distance);
                var rt = await db.GeoRadiusAsync(key, 4, 10000, GeoUnit.Meters);
                rt.ToList().ForEach(r => Console.WriteLine($"#{r.Member}\t,{r.Distance}"));
            }
        }

        private static async Task RedisDistributedLockTestAsync()
        {
            using (var myConn = await ConnectionMultiplexer.ConnectAsync(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                var token = Environment.MachineName;
                if (await db.LockTakeAsync(token, "lock1", TimeSpan.FromSeconds(10)))
                {
                    try
                    {
                        Thread.Sleep(5000);
                        Console.WriteLine("拿到锁了");
                        Thread.Sleep(5000);
                    }
                    catch (Exception)
                    {

                        throw;
                    }
                    finally
                    {
                        await db.LockReleaseAsync(token, "lock1");
                    }
                }
                else
                {
                    Console.WriteLine("获取锁失败！");
                }
            }
        }


        private static async Task RedisBatchTestAsync()
        {
            using (var myConn = await ConnectionMultiplexer.ConnectAsync(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                var batch = db.CreateBatch();
                await db.HashSetAsync($"{KeyPrefix}:hash1", "Desc", "Are u kidding me?");
                await db.HashSetAsync($"{KeyPrefix}:hash1", "Name", "gainorloss");
                await db.HashSetAsync($"{KeyPrefix}:hash1", "Age", "25");
                await db.HashSetAsync($"{KeyPrefix}:hash1", "Birthday", "19930308");
                await db.HashSetAsync($"{KeyPrefix}:hash1", "Account", "Mole");
                batch.Execute();
            }
        }

        private static async Task RedisHashTestAsync()
        {
            using (var myConn = await ConnectionMultiplexer.ConnectAsync(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                await db.HashSetAsync($"{KeyPrefix}:hash1", "Content", "Are u kidding me?");
            }
        }

        private static async Task RedisSortedSetTestAsync()
        {
            using (var myConn = await ConnectionMultiplexer.ConnectAsync(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                var key = $"{KeyPrefix}:HotRanking";
                await db.SortedSetIncrementAsync(key, "解忧杂货铺", 1);
                await db.SortedSetIncrementAsync(key, "解忧杂货铺", 1);
                await db.SortedSetIncrementAsync(key, "解忧杂货铺", 1);
                await db.SortedSetIncrementAsync(key, "解忧杂货铺", 1);
                await db.SortedSetIncrementAsync(key, "解忧杂货铺", 1);
                await db.SortedSetIncrementAsync(key, "朝花夕拾", 1);
                await db.SortedSetIncrementAsync(key, "朝花夕拾", 1);
                await db.SortedSetIncrementAsync(key, "朝花夕拾", 1);
                await db.SortedSetIncrementAsync(key, "朝花夕拾", 1);
                await db.SortedSetIncrementAsync(key, "朝花夕拾", 1);
                while (true)
                {
                    Console.WriteLine("您要搜索的书是：");
                    var input = Console.ReadLine();
                    if (!string.IsNullOrEmpty(input))
                    {
                        await db.SortedSetIncrementAsync(key, input, 1);
                        var entries = await db.SortedSetRangeByRankWithScoresAsync(key, order: Order.Descending);
                        Console.ForegroundColor = ConsoleColor.DarkRed;
                        entries.ToList()
                            .ForEach(i =>
                            {
                                Console.WriteLine($"{i.Element},点击数：{i.Score}");
                            });
                        Console.ForegroundColor = ConsoleColor.White;
                    }
                }
            }
        }

        private static async Task RedisSetTestAsync()
        {
            using (var myConn = await ConnectionMultiplexer.ConnectAsync(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                var key = $"{KeyPrefix}:Sort1";
                await db.SetAddAsync(key, "中国");
                await db.SetAddAsync(key, "美国");
                await db.SetAddAsync(key, "日本");
                await db.SetAddAsync(key, "德国");
                await db.SetAddAsync(key, "法国");
                var len = await db.SetLengthAsync(key);
                var members = await db.SetMembersAsync(key);
                var b = await db.SetRemoveAsync(key, "日本");
            }
        }

        private static async Task RedisListQueueTestAsync()
        {
            using (var myConn = await ConnectionMultiplexer.ConnectAsync(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                var key = $"{KeyPrefix}:EmailQueue";
                while (true)
                {
                    Console.WriteLine("请输入您的任务：");
                    var input = Console.ReadLine();
                    if (!string.IsNullOrEmpty(input))
                    {
                        await db.ListLeftPushAsync(key, input);
                    }
                }
                //var vals = await db.ListRangeAsync(key, 0, -1);
                //var val = await db.ListLeftPopAsync(key);
                //val = await db.ListRightPopAsync(key);
            }
        }

        private static async Task RedisListTestAsync()
        {
            using (var myConn = await ConnectionMultiplexer.ConnectAsync(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                var key = $"{KeyPrefix}:list1";
                await db.ListLeftPushAsync(key, new List<RedisValue>() { "gainorloss", "This is a test about how to use list store in redis server", "ZhangJian" }.ToArray());
                var vals = await db.ListRangeAsync(key, 0, -1);
                var val = await db.ListLeftPopAsync(key);
                val = await db.ListRightPopAsync(key);
            }
        }

        private static async Task RedisClickCountAsync()
        {
            using (var myConn = ConnectionMultiplexer.Connect(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                var id = 1;
                var key_ = $"{KeyPrefix}:clickcountAddress_{id}_{RedisConnectionString.Substring(0, RedisConnectionString.IndexOf(":"))}";
                if (!await db.KeyExistsAsync(key_))
                {
                    var key = $"{KeyPrefix}:clickcount_{id}";
                    await db.StringIncrementAsync(key, 1);

                    await db.StringSetAsync(key_, RedisConnectionString, TimeSpan.FromDays(1));
                }
            }
        }

        private static async Task RedisTestAsync()
        {
            using (var myConn = ConnectionMultiplexer.Connect(RedisConnectionString))
            {
                var db = myConn.GetDatabase();
                await db.StringSetAsync("name", "gainorloss");

                var kvs = new KeyValuePair<RedisKey, RedisValue>[3];
                kvs[0] = new KeyValuePair<RedisKey, RedisValue>("name", "zj");
                kvs[1] = new KeyValuePair<RedisKey, RedisValue>("id", "2");
                kvs[2] = new KeyValuePair<RedisKey, RedisValue>("age", "25");
                await db.StringSetAsync(kvs);
            }
        }

        private static async Task MongoDBTestAsync()
        {
            //IMongoClient client = new MongoClient("mongodb://140.143.88.100:27017");
            //IMongoDatabase db = client.GetDatabase("test");
            //IMongoCollection<Person> persons = db.GetCollection<Person>("Person");
            var persons = new MongoTestContext().Person;
            await persons.InsertOneAsync(new Person() { Age = 25, Name = "gainorloss" });
            {
                //await persons.DeleteManyAsync(Builders<Person>.Filter.Where(p => !string.IsNullOrEmpty(p.Name)));
                await persons.DeleteManyAsync(new BsonDocument());
            }
            {
                var list = new List<Person>();
                var names = new string[] { "gainorloss", "gain", "Zj" };
                for (int i = 0; i < 100; i++)
                {
                    list.Add(new Person() { Age = new Random(Guid.NewGuid().GetHashCode()).Next(1, 100), Name = names[i % 3] });
                }
                await persons.InsertManyAsync(list);
            }
            {
                var cursor = await persons.FindAsync(Builders<Person>.Filter.Where(p => p.Name.Equals("gainorloss")));
                var rt = await cursor.ToListAsync();
            }
            {
                var count = await persons.CountDocumentsAsync(new BsonDocument());
                var filter = Builders<Person>.Filter.Where(p => p.Name.Equals("gain"));
                var options = new FindOptions<Person, Person>();
                options.Limit = 2;
                options.Skip = 3;
                options.Sort = Builders<Person>.Sort.Ascending(p => p.Age).Descending(p => p.Name);
                var cursor = await persons.FindAsync(filter, options);
                var list = await cursor.ToListAsync();
                //cursor.
            }
            {
                var filter = Builders<Person>.Filter.Where(p => p.Name.Equals("gain"));
                var cursor = await persons.FindAsync(filter);
                var list = await cursor.ToListAsync();
                await persons.UpdateManyAsync(filter, Builders<Person>.Update.Set(p => p.Name, "ZhangJian").Set(p => p.Age, 1000));

                var _cursor = await persons.FindAsync(Builders<Person>.Filter.Where(p => p.Name.Equals("ZhangJian")));
                var _list = await _cursor.ToListAsync();
            }
        }

        private static void HystrixCommandInterceptorAttrTest()
        {
            //ProxyGeneratorBuilder builder = new ProxyGeneratorBuilder();
            //using (var generator = builder.Build())
            //{
            //    var p = generator.CreateClassProxy<Person>();
            //    p.SayGoodBye("Nobody");
            //}
            {
                var service = _container.Resolve<ITestService>();
                service.SayHello("gainorloss");
            }
        }

        private static void JwtTest()
        {
            var payload = new Dictionary<string, object>{
                { "Id",1},
                { "Name","gainorloss"}
            };

            const string secret = "gainorloss";

            IJwtAlgorithm jwtAlgorithm = new HMACSHA256Algorithm();
            var jsonSerializer = new JsonNetSerializer();
            IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
            IJwtEncoder jwtEncoder = new JwtEncoder(jwtAlgorithm, jsonSerializer, urlEncoder);
            var token = jwtEncoder.Encode(payload, secret);
            Console.WriteLine(token);

            var builder = new JwtBuilder()
                .WithAlgorithm(new HMACSHA256Algorithm())
                //.WithSerializer(new JsonNetSerializer())
                //.WithUrlEncoder(new JwtBase64UrlEncoder())
                .WithSecret(secret);

            var token_ = builder
                .AddClaim("Id", 1)
                .AddClaim("Name", "gainorloss")
                .Build();
            Console.WriteLine(token.Equals(token_));
            //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            var payload_ = builder.MustVerifySignature().Decode(token_);
            Console.WriteLine(payload_);
            var payload__ = builder.MustVerifySignature().Decode<IDictionary<string, object>>(token_);
            Console.WriteLine(payload__["Id"]);
        }

        private static void AspectTest()
        {
            ProxyGeneratorBuilder proxyGeneratorBuilder = new ProxyGeneratorBuilder();
            using (IProxyGenerator generator = proxyGeneratorBuilder.Build())
            {
                var p = generator.CreateClassProxy<Person>();
                var name = "gainorloss";
                p.SayHi(name);
                p.SayHello(name);
            }
        }

        private static void PollyTest2()
        {
            var policyException = Policy.Handle<Exception>()
                .WaitAndRetry(5, retryCount => TimeSpan.FromSeconds(retryCount));
            var policyTimeOut = policyException.Wrap(Policy.Timeout(3, Polly.Timeout.TimeoutStrategy.Pessimistic));

            policyTimeOut.Execute(() =>
            {
                Console.WriteLine("//*******开始执行");
                Thread.Sleep(5000);
                Console.WriteLine("//*******结束执行");
            });
        }

        private static void PollyTest()
        {
            ISyncPolicy policy = Policy.Handle<ArgumentException>()
                  .Fallback(() =>
                  {
                      Console.WriteLine("失败了");
                  }, ex =>
                  {
                      Console.WriteLine(ex.Message);
                  });
            ISyncPolicy policy_ = Policy.Handle<ArgumentException>()
                  .Retry(5, (ex, count) =>
                  {
                      Console.WriteLine($"正在重试第{count}次");
                  });
            var wrap = policy.Wrap(policy_);
            wrap.Execute(() =>
            {
                throw new ArgumentException("ah");
            });
        }

        private static void PortTest()
        {
            //IsPortInUse();
            var port = GetVariablePort();
        }

        /// <summary>
        /// Get variable port.
        /// </summary>
        private static int GetVariablePort()
        {
            var random = new Random();
            while (true)
            {
                var port = random.Next(10000, 65535);
                if (IsPortInUse(port))
                {
                    return port;
                }
            }
        }

        private static bool IsPortInUse(int port = 80)
        {
            var props = IPGlobalProperties.GetIPGlobalProperties();
            if (props.GetActiveTcpListeners().Any(p => p.Port == port))
            {
                return true;
            };
            if (props.GetActiveUdpListeners().Any(p => p.Port == port))
            {
                return true;
            }
            if (props.GetActiveTcpConnections().Any(p => p.LocalEndPoint.Port == port))
            {
                return true;
            }
            return false;
        }

        private static void Test()
        {
            var name = "userapi";
            var consul = new ConsulClient(cfg => cfg.Address = new Uri("http://localhost:8500"));
            var services = consul.Agent.Services().Result.Response;
            foreach (var service in services)
            {
                Console.WriteLine($"{service.Key},\t{service.Value.Service}");
            }
            var userApi = consul.Agent.Services().Result.Response.FirstOrDefault(s => s.Value.Service.Equals(name));
            Console.WriteLine($"{userApi.Key},\t{userApi.Value.Service}");

            var userApis = consul.Agent.Services().Result.Response.Where(s => s.Value.Service.Equals(name));
            int index = Convert.ToInt32(DateTime.Now.Ticks % (userApis.Count()));
            var userApi_ = userApis.ElementAt(index);
        }
    }
}
